package com.x.compute.core.util;


import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.x.compute.common.constant.VarTypeEnum;
import com.x.compute.common.constant.StrConstant;
import com.x.compute.common.constant.YesOrNoEnum;
import com.x.compute.common.util.ComputeUtils;
import com.x.compute.core.annotation.ComputeTable;
import com.x.compute.core.annotation.ComputeVar;
import com.x.compute.core.context.VariableContext;
import com.x.compute.core.entity.ComputeExpression;
import com.x.compute.core.entity.ComputeParam;
import com.x.compute.core.entity.Variable;
import lombok.extern.log4j.Log4j2;

import java.lang.reflect.Field;
import java.util.*;

/**
 * 计算参数生成工具类
 * @author xuxin
 */
@Log4j2
public class ComputeParamUtils {

    private ComputeParamUtils() {
        throw new IllegalStateException("Utility class");
    }

    /**
     * 构建不同类型变量的计算参数
     * @param businessId
     * @param var
     * @param expression
     * @return
     */
    public static ComputeParam buildVar(String businessId,
                                        String tableId,
                                        String var,
                                        VarTypeEnum varType,
                                        ComputeExpression expression){
        return ComputeParam.builder()
                .id(IdUtil.getSnowflakeNextId())
                .businessId(businessId.toString())
                .var(var)
                .tableId(tableId)
                .expression(expression.getExpression())
                .varType(varType)
                .originalExpression(expression.getOriginalExpression())
                .build();
    }

    /**
     * 构建不同类型变量的计算参数
     * @param businessId
     * @param var
     * @param expression
     * @return
     */
    public static ComputeParam buildVar(String businessId,
                                        String tableId,
                                        String var,
                                        VarTypeEnum varType,
                                        String expression){
        return ComputeParam.builder()
                .id(IdUtil.getSnowflakeNextId())
                .businessId(businessId.toString())
                .var(var)
                .tableId(tableId)
                .expression(expression)
                .varType(varType)
                .build();
    }

    /**
     * 通过反射构建计算参数，
     * 前提需要映射的字段在source与target中的字段名一样，同时source的字段类型为ComputeExpression
     * @param businessId
     * @param source
     * @param target
     * @return
     */
    public static List<ComputeParam> buildVars(String businessId, Object source, Object target){
        List<ComputeParam> paramList = new ArrayList<>();
        Class clazz = target.getClass();
        ComputeTable typeAnnotation = (ComputeTable) clazz.getAnnotation(ComputeTable.class);
        String tableId = typeAnnotation.id();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            ComputeVar varAnnotation = field.getAnnotation(ComputeVar.class);
            if (varAnnotation == null){
                continue;
            }
            try {
                field.setAccessible(true);
                String varName = varAnnotation.name();
                varName = StrUtil.isNotBlank(varName) ? varName : field.getName();
                Field sourceField = source.getClass().getDeclaredField(field.getName());
                sourceField.setAccessible(true);
                Object fieldValue = sourceField.get(source);
                ComputeParam param;
                if (fieldValue instanceof ComputeExpression){
                    ComputeExpression expression = (ComputeExpression) fieldValue;
                    param = buildVar(businessId, tableId, varName, varAnnotation.type(), expression);
                }else {
                    param = buildWithoutVar(businessId, tableId, varName, varAnnotation.type(), fieldValue.toString());
                }
                if (field.getType().equals(Long.class)){
                    field.set(target, param.getId());
                }
                paramList.add(param);
            }catch (IllegalAccessException e){

            } catch (NoSuchFieldException e) {

            }
        }
        return paramList;
    }

    /**
     * 构建无引用变量的计算参数
     * @param businessId 业务id
     * @param tableId 表格id
     * @param var 计算字段
     * @param varType 字段类型
     * @param value 计算字段值
     * @return
     */
    public static ComputeParam buildWithoutVar(String businessId,
                                               String tableId,
                                               String var,
                                               VarTypeEnum varType,
                                               Object value) {
        String expression = varType.equals(VarTypeEnum.STRING) ? StrUtil.wrap(value.toString(), StrConstant.SINGLE_QUOTES) : value.toString();
        return buildVar(businessId, tableId, var, varType, expression);
    }

    /**
     * 构建无引用变量的文本类型的计算参数
     * @param businessId 业务id
     * @param var 计算字段
     * @param value 计算字段值
     * @return
     */
    public static ComputeParam buildWithoutVar(String businessId,
                                               String tableId,
                                               String var,
                                               String value) {
        String expression = StrUtil.wrap(value, StrConstant.SINGLE_QUOTES);
        return buildVar(businessId, tableId, var, VarTypeEnum.STRING, expression);
    }

    /**
     * 构建无引用变量的文本类型的计算参数
     * @param businessId 业务id
     * @param var 计算字段
     * @param value 计算字段值
     * @return
     */
    public static ComputeParam buildWithoutVar(String businessId,
                                               Class clazz,
                                               String var,
                                               String value) {
        String expression = StrUtil.wrap(value, StrConstant.SINGLE_QUOTES);
        return buildVar(businessId, getTableId(clazz), var, VarTypeEnum.STRING, expression);
    }

    /**
     * 构建无引用变量的布尔类型的计算参数
     * @param businessId 业务id
     * @param var 计算字段
     * @param value 计算字段值
     * @return
     */
    public static ComputeParam buildWithoutVar(String businessId,
                                               String tableId,
                                               String var,
                                               Boolean value) {
        String expression = StrUtil.wrap(YesOrNoEnum.getChineseValue(value), StrConstant.SINGLE_QUOTES);
        return buildVar(businessId, tableId, var, VarTypeEnum.STRING, expression);
    }

    /**
     * 构建无引用变量的数值类型的计算参数
     * @param businessId 业务id
     * @param var 计算字段
     * @param value 计算字段值
     * @return
     */
    public static ComputeParam buildWithoutVar(String businessId,
                                               String tableId,
                                               String var,
                                               Number value) {
        return buildVar(businessId, tableId, var, VarTypeEnum.NUMBER, value.toString());
    }

    /**
     * 构建数值类型的计算参数
     * @param businessId
     * @param var
     * @param expression
     * @return
     */
    public static ComputeParam buildNumberVar(String businessId,
                                              String tableId,
                                              String var,
                                              ComputeExpression expression){
        return buildVar(businessId, tableId, var, VarTypeEnum.NUMBER, expression);
    }

    /**
     * 构建数值类型的计算参数
     * @param businessId
     * @param var
     * @param expression
     * @return
     */
    public static ComputeParam buildNumberVar(String businessId,
                                              Class clazz,
                                              String var,
                                              ComputeExpression expression){
        return buildVar(businessId, getTableId(clazz), var, VarTypeEnum.NUMBER, expression);
    }

    /**
     * 构建数值类型的计算参数
     * @param businessId
     * @param var
     * @param expression
     * @return
     */
    public static ComputeParam buildNumberVar(String businessId,
                                              String tableId,
                                              String var,
                                              String expression){
        return buildVar(businessId, tableId, var, VarTypeEnum.NUMBER, expression);
    }

    /**
     * 构建文本类型的计算参数
     * @param businessId
     * @param var
     * @param expression
     * @return
     */
    public static ComputeParam buildStringVar(String businessId,
                                              String tableId,
                                              String var,
                                              ComputeExpression expression){
        return buildVar(businessId, tableId, var, VarTypeEnum.STRING, expression);
    }

    /**
     * 构建日期类型的计算参数
     * @param businessId
     * @param var
     * @param expression
     * @return
     */
    public static ComputeParam buildDateVar(String businessId,
                                            String tableId,
                                            String var,
                                            ComputeExpression expression){
        return buildVar(businessId, tableId, var, VarTypeEnum.DATE, expression);
    }

    /**
     * 构建日期类型的计算参数
     * @param businessId
     * @param var
     * @param expression
     * @return
     */
    public static ComputeParam buildDateVar(String businessId,
                                            String tableId,
                                            String var,
                                            String expression){
        return buildVar(businessId,tableId,var, VarTypeEnum.DATE,expression);
    }

    /**
     * 构建计算列参数
     * @param businessId
     * @param valueMap
     * @param columnMap
     * @return
     */
    public static Map<String, Map<String,Object>> buildColumnParam(String businessId,
                                                                   Map<String, Object> valueMap,
                                                                   Map<String,String> columnMap){
        Map<String, Map<String,Object>> columnValueMap = new HashMap<>();
        columnMap.forEach((k,v) ->{
            Map<String,Object> map = new HashMap<>();
            map.put(businessId.toString(), valueMap.get(k));
            columnValueMap.put(v, map);
        });
        return columnValueMap;
    }

    /**
     * 获取tableId
     * @param clazz
     * @return
     */
    private static String getTableId(Class clazz) {
        String tableId = VariableContext.classRefTableMap.get(clazz);
        Assert.notBlank(tableId);
        return tableId;
    }
}
